package cn.tedu.epidemic.webapi.filter;

import cn.tedu.community.commons.restful.JsonResult;
import cn.tedu.community.commons.restful.ServiceCode;
import cn.tedu.epidemic.webapi.security.LoginPrincipal;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

/**
 * 处理jwt的过滤器类
 * <p>此类的主要职责</p>
 * <ul>
 *     <li>接收客户端可能提交的jwt</li>
 *     <li>尝试解析客户端提交的jwt</li>
 *     <li>将解析得到的结果存入到SecurityContext</li>
 * </ul>
 */
@Component
@Slf4j
public class JwtAuthorizationFilter extends OncePerRequestFilter {

    @Value("${community.jwt.secret-Key}")
    private String secretKey;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //清除SecurityContext中的数据
        SecurityContextHolder.clearContext();
        //根据业内惯用的做法，客户端应该将JWT保存在请求头中(Request Header)中的名为Authorization的属性中
        String jwt = request.getHeader("Authorization");
        log.debug("尝试接受客户端携带的JWT数据，JWT：{}", jwt);
        //判断客户端是否提交了有效的jwt
        if (!StringUtils.hasText(jwt) || jwt.length() < 113) {
            //直接放行
            filterChain.doFilter(request, response);
            return;
        }
        //尝试解析jwt
        //问题1:secretKey不应该反复定义
        //问题2:解析过程中可能抛出异常
        Claims claims = null;
        try {
            claims = Jwts.parser()
                    .setSigningKey(secretKey)
                    .parseClaimsJws(jwt)
                    .getBody();
        } catch (SignatureException e) {
            response.setContentType("application/json;charset=utf-8");
            String message = "非法访问！";
            JsonResult<Void> jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_SIGNATURE, message);
            PrintWriter printWriter = response.getWriter();
            printWriter.print(JSON.toJSONString(jsonResult));
            printWriter.close();
            return;
        } catch (MalformedJwtException e) {
            response.setContentType("application/json;charset=utf-8");
            String message = "非法访问！";
            log.warn("解析jwt时出现MalformedJwtException，响应：{}", message);
            JsonResult<Void> jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_MALFORMED, message);
            PrintWriter printWriter = response.getWriter();
            printWriter.print(JSON.toJSONString(jsonResult));
            printWriter.close();
            return;
        } catch (ExpiredJwtException e) {
            response.setContentType("application/json;charset=utf-8");
            String message = "你的账号信息已过期，请重新登录！";
            log.warn("解析jwt时出现ExpiredJwtException，响应：{}", message);
            JsonResult<Void> jsonResult = JsonResult.fail(ServiceCode.ERR_JWT_EXPIRED, message);
            PrintWriter printWriter = response.getWriter();
            printWriter.print(JSON.toJSONString(jsonResult));
            printWriter.close();
            return;
        }
        Long id = claims.get("id", Long.class);
        String userName = claims.get("username", String.class);
        log.debug("从jwt中解析得到的用户ID：{}", id);
        log.debug("从jwt中解析得到的用户名：{}", userName);

        //基于解析jwt的结果创建认证信息
        LoginPrincipal principal = new LoginPrincipal();//使用用户名作为当事人数据
        principal.setId(id);
        principal.setUsername(userName);
        Object credentials = null;
        List<GrantedAuthority> authorities = new ArrayList<>();
        SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority("暂时给个山寨权限！");
        authorities.add(simpleGrantedAuthority);
        Authentication authentication = new UsernamePasswordAuthenticationToken(principal, credentials, authorities);
        //将认证信息存入到SecurityContext中
        SecurityContext securityContext = SecurityContextHolder.getContext();
        securityContext.setAuthentication(authentication);
        //过滤器链继承后向后执行，即：放行
        //如果没有执行以下代码，表示”阻止“,即处理请求的过程到此结束，在浏览器过程中将显示一片空白
        filterChain.doFilter(request, response);
    }
}
